#include <stdio.h>
#include "../quickjs.h"

#define JS_INIT_MODULE js_init_module
#define countof(x) (sizeof(x) / sizeof((x)[0]))

// 接受一个js的数组
// JS_IsArray判断是否数组类型
// JS_VALUE_GET_OBJ 获取对象结构 数组继承对象
// JS_GetPropertyStr length 获取数组长度属性
// JS_IsException是否成功获取长度
// JS_GetPropertyUint32
// argv[0] -> JS_VALUE_GET_OBJ -> cString
// JS_GetPropertyStr
// JS_GetPropertyUint32
static JSValue js_array_to_c(
    JSContext *js_ctx,
    JSValueConst this_val,
    int argc, JSValueConst *argv)
{

    if (!JS_IsArray(js_ctx, argv[0]))
    {
        printf("JS_IsArray fail\n");
        return JS_ThrowTypeError(js_ctx, "argv[0] not array");
    }

    // JS_VALUE_GET_OBJ 获取对象结构 数组继承对象
    JSObject *obj = JS_VALUE_GET_OBJ(argv[0]);

    // JS_GetPropertyStr length 获取数组长度属性
    JSValue length = JS_GetPropertyStr(js_ctx, argv[0], "length");

    // JS_IsException 检查函数执行结果是否异常
    if (JS_IsException(length))
    {
        printf("js_array_to_c: setJS_GetProperty JS_ATOM_length fail\n");
        // 异常处理
        return JS_ThrowInternalError(js_ctx, "array without length property");
    }

    uint32_t len = 0;
    if (JS_ToUint32(js_ctx, &len, length))
    {
        printf("js_array_to_c: JS_ToUint32 fail\n");
        return JS_UNDEFINED;
    }
    // 需要释放属性对象
    JS_FreeValue(js_ctx, length);
    // 打印长度信息
    printf("js_array_to_c: len=%u\n", len);

    // 遍历
    uint32_t i = 0;
    for (i = 0; i < len; ++i)
    {
        // 读取值
        JSValue val = JS_GetPropertyUint32(js_ctx, argv[0], i);
        if (JS_IsException(val))
        {
            printf("set_array: JS_GetPropertyUint32 fail, i=%u\n", i);
            return JS_UNDEFINED;
        }

        const char *val_str = JS_ToCString(js_ctx, val);
        if (val_str == NULL)
        {
            printf("set_array: JS_ToCString fail, i=%u\n", i);
            return JS_UNDEFINED;
        }

        printf("set_array: [%u] = %s\n", i, val_str);
        JS_FreeCString(js_ctx, val_str);
        JS_FreeValue(js_ctx, val);
    }

    return JS_UNDEFINED;
}
// 返回一个数组到js
// JS_NewArray创建一个js的数组对象
// JS_SetPropertyUint32 设置属性
// val -> JS_NewArray -> jsArray
// JS_SetPropertyUint32
static JSValue c_array_to_js(
    JSContext *js_ctx,
    JSValueConst this_val,
    int argc, JSValueConst *argv)
{
    JSValue arr = JS_NewArray(js_ctx);

    uint32_t idx = 0;
    for (idx = 0; idx < 3; ++idx)
    {
        JS_SetPropertyUint32(js_ctx, arr, idx, JS_NewUint32(js_ctx, idx * 10));
    }

    // array with diffrent data type.
    JS_SetPropertyUint32(js_ctx, arr, idx, JS_NewString(js_ctx, "hello from get_array"));

    return arr;
}

// 注册一个函数

// 1 函数列表
static const JSCFunctionListEntry js_test_funcs[] = {
    /* JS_CFUNC_DEF(函数入口名称，入参个数，QuickJS C 函数) */
    JS_CFUNC_DEF("js_array_to_c", 1, js_array_to_c),
    JS_CFUNC_DEF("c_array_to_js", 0, c_array_to_js),
};

// 2 函数注册
static int js_module_init(JSContext *ctx, JSModuleDef *m)
{
    return JS_SetModuleExportList(
        ctx, m,
        js_test_funcs, countof(js_test_funcs));
}

// 3 模块导出
#define JS_INIT_MODULE js_init_module
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)
{
    JSModuleDef *m;
    m = JS_NewCModule(ctx, module_name, js_module_init);
    if (!m)
        return NULL;
    JS_AddModuleExportList(ctx, m, js_test_funcs, countof(js_test_funcs));
    return m;
}